package framework.util.easypoi;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

import cn.afterturn.easypoi.excel.annotation.Excel;
import com.esotericsoftware.reflectasm.MethodAccess;
import framework.util._BeanUtil;
import framework.util.layui._LayuiUtil;
import framework.util.queryFilter.service.IBaseService;
import framework.util.queryFilter.service.impl.QueryFilter;
import framework.util.queryFilter.vo.Where;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.util.CollectionUtils;


/**
 * easypoi的工具类
 *
 * @author 吴宇亮
 * @date 2019年1月23日
 */
public class _EasypoiUtil {

	/**
	 * 对即将导入的数据，校验指定的字段是有符合外键关联。
	 * <br/>对list中的每条数据的foreignText，都要在relatedTable中找到对应relatedText的记录。如果没找到，则会添加到错误信息中；如果找到了，则会将对应记录的"Lid"设置到list的每条记录的foreignKey中
	 * <br/>使用示例：_EasypoiUtil.checkAndSetForeignKey(result.getList(), startRowNum, "typeLid","typeName", yxcGoodsTypeService, "name", "商品类别");
	 *
	 * @param list 要校验的list
	 * @param startRowNum excel数据从第几行开始，用于错误提示
	 * @param relatedService 被关联表对应的service
	 * @param foreignText 外键text字段名
	 * @param relatedText 被关联text字段名
	 * @param foreignKey 外键主键字段名
	 */
	public static<T> StringBuilder checkAndSetForeignKey(List<T> list, int startRowNum, IBaseService relatedService, String foreignText, String relatedText
			, String foreignKey) throws NoSuchFieldException {
		return checkAndSetForeignKey(list, startRowNum, relatedService, foreignText, relatedText, "lid");
	}

	/**
	 * 对即将导入的数据，校验指定的字段是有符合外键关联。
	 * <br/>对list中的每条数据的foreignText，都要在relatedTable中找到对应relatedText的记录。如果没找到，则会添加到错误信息中；如果找到了，则会将对应记录的relatedKey设置到list的每条记录的foreignKey中
	 * <br/>使用示例：_EasypoiUtil.checkAndSetForeignKey(result.getList(), startRowNum, "typeLid","typeName", yxcGoodsTypeService, "name", "商品类别");
	 *
	 * @param list 要校验的list
	 * @param startRowNum excel数据从第几行开始，用于错误提示
	 * @param relatedService 被关联表对应的service
	 * @param foreignText 外键text字段名
	 * @param relatedText 被关联text字段名
	 * @param foreignKey 外键主键字段名
	 * @param relatedKey 被关联主键字段名
	 */
	public static<T> StringBuilder checkAndSetForeignKey(List<T> list, int startRowNum, IBaseService relatedService, String foreignText, String relatedText
			, String foreignKey, String relatedKey) throws NoSuchFieldException {
		StringBuilder errorMsg = new StringBuilder();
		if(CollectionUtils.isEmpty(list)){
			return errorMsg;
		}
		Class<?> clazz = list.get(0).getClass();
		//被导入的字段的中文名
		String chineseName = clazz.getDeclaredField(foreignText).getAnnotation(Excel.class).name();
		//ASM反射
		MethodAccess methodAccess = MethodAccess.get(clazz);
		MethodAccess relatedMethodAccess = MethodAccess.get(relatedService.getPersistType());
		//value和lid的方法名
		String foreignTextGetterMethodName = _BeanUtil.getGetterMethodName(foreignText);
		String foreignKeySetterMethodName = _BeanUtil.getSetterMethodName(foreignKey);
		String relateKeyGetterMethodName = _BeanUtil.getGetterMethodName(relatedKey);

		QueryFilter filter = new QueryFilter(_LayuiUtil.FIRST_PAGE, Integer.MAX_VALUE);
		for (T t : list) {
			startRowNum++;
			String value = (String)methodAccess.invoke(t, foreignTextGetterMethodName);
			if(StringUtils.isBlank(value)){
				errorMsg.append("第" + (startRowNum - 1) + "行：【" + chineseName + "】列不能为空<br/>");
				continue;
			}

			filter.where(relatedText, Where.eq, value);
			List all = relatedService.getAll(filter);
			filter.clearWhereList();
			if(all.size() == 0){
				//系统中不存在【大米类】商品类别； 系统中不存在【猪肉】肉类
				errorMsg.append("第" + (startRowNum - 1) + "行：系统中不存在【" + value + "】的" + chineseName + "<br/>");
				continue;
			}
			Object lid = relatedMethodAccess.invoke(all.get(0), relateKeyGetterMethodName);
			methodAccess.invoke(t, foreignKeySetterMethodName, lid);
		}
		return errorMsg;
	}


	/**
	 * TODO 校验联合主键，联合主键
	 * @param list 要校验的list
	 * @param startRowNum excel数据从第几行开始
	 * @param service 对应表的service
	 * @param privateKeyChineseNames 要校验字段的中文名称Arr
	 * @param privateKeyFieldNames 要校验字段的名称Arr
	 * @param isOverrideImport 是否为覆盖导入
	 * @return
	 */
	public static<T> StringBuilder checkPrimaryKey(List<T> list, int startRowNum, IBaseService<T, ?> service, String[] privateKeyChineseNames, String[] privateKeyFieldNames, Boolean isOverrideImport) {
		return null;
	}

	/**
	 * 对即将导入的数据，校验指定的字段符合主键唯一性。主要对：1导入的数据中是否唯一；2数据库中是否唯一 进行校验。使用示例如下：
	 * <code>
	 *     StringBuilder errorMsg = _EasypoiUtil.checkPrimaryKey(result.getList(), startRowNum, service, "条形码编号", "barcode");
	 *     if(errorMsg.length() > 0){
	 *     		return sendFail(errorMsg.toString(), ResponseVo.LAYER_TYPE_OPEN);
	 *     }
	 * </code>
	 *
	 * @param list 要校验的list
	 * @param startRowNum excel数据从第几行开始
	 * @param service 对应表的service
	 * @param privateKeyChineseName 要校验字段的中文名称
	 * @param privateKeyFieldName 要校验字段的名称
	 * @param isOverrideImport 是否为覆盖导入
	 *
	 * @return StringBuilder 如果没有唯一性冲突，则返回length==0的StringBuilder；否则返回带有错误信息的StringBuilder
	 * @author 吴宇亮
	 * @date 2019年1月24日
	 */
	public static<T> StringBuilder checkPrimaryKey(List<T> list, int startRowNum, IBaseService<T, ?> service, String privateKeyChineseName, String privateKeyFieldName, Boolean isOverrideImport)
			throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException {
		StringBuilder errorMsg = new StringBuilder();
		//判断是否表中及数据库id是否唯一，并且要判断的id不能为null
		for(int i = 0, j = list.size(); i < j; i++){
			String value = BeanUtils.getProperty(list.get(i), privateKeyFieldName);
			if(null == value){
				continue;
				//注释原因。不能为null已经被基本校验过滤了（基本校验可以用正则规定一定要有字符）
				//throw new IllegalArgumentException("【"+service.getPersistType().getName()+"."+privateKeyFieldName+"】字段为null！要校验唯一性的字段不能为null！");
			}
			//判断导入的数据是否重复
			boolean isContinue = false;
			for(int n = i + 1, m = list.size(); n < m; n++){
				String value2 = BeanUtils.getProperty(list.get(n), privateKeyFieldName);
				if(value.equals(value2)){
					errorMsg.append("第"+(i+startRowNum)+"行 和 第"+(n+startRowNum)+"行:【"+privateKeyChineseName+"】为\""+value+"\"重复了<br/>");
					isContinue = true;
					break;
				}
			}
			//excel中已经重复了，就不校验数据库了
			if(isContinue){
				continue;
			}

			if(!isOverrideImport){
				//如果不为覆盖导入，还需要判断数据库中是否有重复的数据
				QueryFilter filter = new QueryFilter(_LayuiUtil.FIRST_PAGE, Integer.MAX_VALUE);
				filter.where(privateKeyFieldName, Where.eq, value);
				if(service.getCount(filter) > 0){
					errorMsg.append("第"+(i+startRowNum)+"行:【"+privateKeyChineseName+"】的\""+value+"\"在系统中已存在!不能重复!<br/>");
				}
			}
		}
		return errorMsg;
	}

	/**
	 * 在ExcelHandler中，自动判断'_变量'是否合法，如果合法就在对应的'变量'中设值；如果'_变量'不合法，则不进行处理，调用者可以抛给hibernateVailtor去自动获取错误提示
	 *
	 * @param obj 要转换的对象
	 * @param transformList 要自动转换的字段名称
	 * @throws Exception
	 * @author 吴宇亮
	 * @date 2019年1月23日
	 */
	public static void transformField(ExcelHelperBo obj, List<ExcelTransformHelper> transformList) throws Exception {
		//TODO 反射优化+代码优化+能不能不依赖getter、setter方法？
		Class<? extends ExcelHelperBo> clazz = obj.getClass();
		
		for (ExcelTransformHelper excelTransformHelper : transformList) {
			String _fieldName = excelTransformHelper.getFieldName();
			Map<String, Integer> transformMap = excelTransformHelper.getTransformMap();
			
			if(!_fieldName.startsWith("_")){
				_fieldName = "_" + _fieldName;
			}
			
			String fieldName = _fieldName.substring(1);
			try {
				//excel中的值为Integer类型的字符串，并且要转成Integer
				if(Integer.class == clazz.getDeclaredField(fieldName).getType() && null == transformMap){
					String property = BeanUtils.getProperty(obj, _fieldName);
					if(NumberUtils.isNumber(property)){
						BeanUtils.setProperty(obj, fieldName, Integer.valueOf(property));
					}
				//excel中的值为String，并且要转为对应的状态Integer
				}else if(Integer.class == clazz.getDeclaredField(fieldName).getType() && null != transformMap){
					String key = BeanUtils.getProperty(obj, _fieldName);
					Integer value = transformMap.get(key);
					BeanUtils.setProperty(obj, fieldName, value);
				//excel中的值为Double类型的字符串，并且要转成Double
				}else if(Double.class == clazz.getDeclaredField(fieldName).getType()){
					String property = BeanUtils.getProperty(obj, _fieldName);
					if(NumberUtils.isNumber(property)){
						BeanUtils.setProperty(obj, fieldName, Double.valueOf(property));
					}
				}
			} catch (NoSuchFieldException e) {
				//没有这个属性，空实现
			} catch (SecurityException e) {
				//空实现
			}
		}
	}



}
